/*
 * Copyright (C) 2015 Square, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.taichuan.selfcheck.utils;

import android.util.Log;

import com.taichuan.selfcheck.eventbus.DownProgressEvent;

import org.greenrobot.eventbus.EventBus;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.concurrent.TimeUnit;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.Buffer;
import okio.BufferedSource;
import okio.ForwardingSource;
import okio.Okio;
import okio.Source;

public final class DownLoadProgress {
    private static final String TAG = DownLoadProgress.class.getSimpleName();

    private static final int CONNECT_TIMEOUT_SECONDS = 30;//连接超时
    private static final int WRITE_TIMEOUT_SECONDS = 10;//写入超时
    private static final int READ_TIMEOUT_SECONDS = 60;//读取超时
    private OkHttpClient mOkHttpClient;
    private ProgressListener mProgressListener;
    private Call mCall = null;

    private DownLoadProgress() {
        initOkHttpClient();
    }

//    public DownLoadProgress(ProgressListener listener) {
//        mProgressListener = listener;
//        mOkHttpClient = new OkHttpClient.Builder()
//                .addNetworkInterceptor(new Interceptor() {
//                    @Override
//                    public Response intercept(Chain chain) throws IOException {
//                        Response originalResponse = chain.proceed(chain.request());
//                        return originalResponse.newBuilder()
//                                .body(new ProgressResponseBody(originalResponse.body(), mProgressListener))
//                                .build();
//                    }
//                })
//                .readTimeout(READ_TIMEOUT_SECONDS, TimeUnit.SECONDS)
//                .build();
//    }

    public static DownLoadProgress get() {
        return ProgressHolder.progress;
    }

    private void initOkHttpClient() {
        final ProgressListener listener = new ProgressListener() {
            @Override
            public void update(long bytesRead, long contentLength, boolean done) {
//                System.out.println("current : " + bytesRead + " , total : " + contentLength + " , "
//                        + (100 * bytesRead) / contentLength + "% done\n");
            }

            @Override
            public void completed(String path, String cachePath) {
                System.out.println("download completed path: " + path + " , cachePath : " + cachePath);
            }

            @Override
            public void failure(String path, String errorMsg) {

            }

            @Override
            public void updateProgress(int progress) {
                System.out.println("updateProgress: " + progress);
                EventBus.getDefault().post(new DownProgressEvent(progress));
            }
        };

        mOkHttpClient = new OkHttpClient.Builder()
                .addNetworkInterceptor(new Interceptor() {
                    @Override
                    public Response intercept(Chain chain) throws IOException {
                        Response originalResponse = chain.proceed(chain.request());
                        return originalResponse.newBuilder()
                                .body(new ProgressResponseBody(originalResponse.body(), listener))
                                .build();
                    }
                })
                .readTimeout(READ_TIMEOUT_SECONDS, TimeUnit.SECONDS)
                .build();
        mProgressListener=listener;
    }

    private OkHttpClient getOkHttpClient() {
        return mOkHttpClient;
    }

    /**
     * 支持断点续传的下载方法
     *
     * @param downloadLength
     * @param url
     * @param cacheFile
     * @throws IOException
     */
    public void executeContinue(long downloadLength, String url, File cacheFile) throws IOException {
        Log.v("zty---", "executeContinue: ApplicationUpgrade 已经下载 = " + downloadLength);
        RandomAccessFile savedFile = new RandomAccessFile(cacheFile, "rw");
        //数据修正
        /*if (downloadLength > savedFile.length()) {
            downloadLength = savedFile.length();
        }*/
        Request request = new Request.Builder().addHeader("RANGE", "bytes=" + downloadLength + "-").url(url).build();
        mCall = mOkHttpClient.newCall(request);
        Response response = mCall.execute();
        if (!response.isSuccessful()) {
            Log.v("zty---", "executeContinue: ApplicationUpgrade 下载请求失败");
            throw new IOException("Unexpected code " + response);
        }
        savedFile.seek(downloadLength);//跳过已经下载的字节
        byte[] bytes = new byte[4096];
        int length = 0;
        try {
            while ((length = response.body().byteStream().read(bytes)) != -1) {
                savedFile.write(bytes, 0, length);
            }
        } finally {
            if (savedFile != null) {
                savedFile.close();
            }
            if (response.body() != null) {
                response.body().close();
                System.out.println("close response.body()!");
            }
        }
        if (mProgressListener != null) {
            Log.v("zty---", "executeContinue: ApplicationUpgrade 下载完成");
            mProgressListener.completed(url, cacheFile.getPath());
        }
    }

    public long execute(String url, File cacheFile) throws IOException {
        long fileSize = 0L;
        Request request = new Request.Builder().url(url).build();
        mCall = mOkHttpClient.newCall(request);
        Response response = mCall.execute();
        if (!response.isSuccessful()) {
            throw new IOException("Unexpected code " + response);
        }
        InputStream inputStream=null;
        FileOutputStream outputStream=null;
        byte[] bytes = new byte[4096];
        int length = 0;
        try {
            outputStream = new FileOutputStream(cacheFile);
            inputStream=response.body().byteStream();
            long totalLength=response.body().contentLength();
            long sum=0;

            while ((length = inputStream.read(bytes)) != -1) {
                outputStream.write(bytes, 0, length);
                sum += length;
                int progress = (int) (sum * 1.0f / totalLength * 100);
                //下载中更新进度条
                if (mProgressListener != null) {
                    mProgressListener.updateProgress(progress);
                }
            }
            fileSize = outputStream.getChannel().size();
        } catch (Exception e){
            e.printStackTrace();
        }finally {
            if(inputStream != null){
                inputStream.close();
                System.out.println("close inputStream!");
            }
            if (outputStream != null) {
                outputStream.flush();
                outputStream.close();
                System.out.println("close outputstream!");
            }
            if (response.body() != null) {
                response.body().close();
                System.out.println("close response.body()!");
            }
        }

        if (mProgressListener != null) {
            mProgressListener.completed(url, cacheFile.getPath());
        }
        return fileSize;
    }

    /**
     * 定义一个方法用于执行异步下载任务
     * @param url
     * @param cacheFile
     * @throws IOException
     */
    public void enqueue(final String url, final File cacheFile) throws IOException {
        Request request = new Request.Builder().url(url).build();
        mCall = mOkHttpClient.newCall(request);
        //异步请求
        mCall.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.e(TAG, "onFailure: 下载失败：  " + e.getMessage());
                if (mProgressListener != null) {
                    mProgressListener.failure(url, e.getMessage());
                }
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (!response.isSuccessful()) {
                    Log.e(TAG, "onResponse: 异步下载异常：  " + response.code());
                    //通知下载失败
                    if (mProgressListener != null) {
                        mProgressListener.failure(url,"下载过程中出现异常" + response.code());
                    }
                    throw new IOException("Unexpected code " + response);
                }
                FileOutputStream outputStream = new FileOutputStream(cacheFile);
                byte[] bytes = new byte[4096];
                int length = 0;
                try {
                    while ((length = response.body().byteStream().read(bytes)) != -1) {
                        outputStream.write(bytes, 0, length);
                    }
                    //下载成功，通知外部组件
                    if (mProgressListener != null) {
                        mProgressListener.completed(url, cacheFile.getPath());
                    }

                } catch (IOException e){
                    e.printStackTrace();
                    Log.e(TAG, "onResponse: 下载过程中出现异常:  " + e.getMessage());
                    //通知下载失败
                    if (mProgressListener != null) {
                        mProgressListener.failure(url,"下载过程中出现异常" + e.getMessage());
                    }
                }
                finally {
                    if (outputStream != null) {
                        outputStream.flush();
                        outputStream.close();
                        System.out.println("close outputstream!");
                    }
                    if (response.body() != null) {
                        response.body().close();
                        System.out.println("close response.body()!");
                    }
                    Log.e(TAG, "onResponse: 下载流程结束，执行finall语句");
                }
            }
        });
    }

    public long getFileSize(String url) throws IOException {
        long fileSize = 0L;
        Request request = new Request.Builder().url(url).build();
        mCall = mOkHttpClient.newCall(request);
        InputStream inputStream = null;
        Response response = mCall.execute();
        byte[] bytes = new byte[1024 * 10];
        int length = -1;
        try {
            inputStream = response.body().byteStream();
            while ((length = inputStream.read(bytes)) != -1) {
                fileSize += length;
            }
        } catch (Exception e) {
            return 0;
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
            if (response.body() != null) {
                response.body().close();
            }
        }
        return fileSize;
    }

    public void cancel() {
        if (mCall != null) {
            Log.e(TAG, "cancel: 取消下载");
            mCall.cancel();
        }
    }


    public RequestProgress createRequestProgress(String url) {
        Request request = new Request.Builder()
                .url(url)
                .build();
        return new RequestProgress(request);
    }

    public interface ProgressListener {
        void update(long bytesRead, long contentLength, boolean done);

        void completed(String path, String cachePath);

        void failure(String path, String errorMsg);

        void updateProgress(int progress);
    }

    private static class ProgressResponseBody extends ResponseBody {

        private final ResponseBody responseBody;
        private final ProgressListener progressListener;
        private BufferedSource bufferedSource;

        public ProgressResponseBody(ResponseBody responseBody, ProgressListener progressListener) {
            this.responseBody = responseBody;
            this.progressListener = progressListener;
        }

        @Override
        public MediaType contentType() {
            return responseBody.contentType();
        }

        @Override
        public long contentLength() {
            return responseBody.contentLength();
        }

        @Override
        public BufferedSource source() {
            if (bufferedSource == null) {
                bufferedSource = Okio.buffer(source(responseBody.source()));
            }
            return bufferedSource;
        }

        private Source source(Source source) {
            return new ForwardingSource(source) {
                long totalBytesRead = 0L;

                @Override
                public long read(Buffer sink, long byteCount) throws IOException {
                    long bytesRead = super.read(sink, byteCount);
                    // read() returns the number of bytes read, or -1 if this source is exhausted.
                    totalBytesRead += bytesRead != -1 ? bytesRead : 0;
                    progressListener.update(totalBytesRead, responseBody.contentLength(), bytesRead == -1);
                    return bytesRead;
                }
            };
        }
    }

    private static final class ProgressHolder {
        private static DownLoadProgress progress = new DownLoadProgress();
    }

    public class RequestProgress {
        Request request;

        private RequestProgress(Request request) {
            this.request = request;
        }
    }
}
